package sharemem

import (
	"sort"
	"strings"
	"sync/atomic"
	"unicode/utf8"

	"server_cluster/common/logger"
	"server_cluster/common/protoc/pb"
)

// Mail 玩家邮件
type Mail struct {
	Id              MyInt32      // 邮件id
	Title           [20]rune     // 邮件标题
	Content         [500]rune    // 邮件内容
	Sender          [10]rune     // 邮件发送者
	SendTime        uint32       // 邮件发送时间戳 秒
	ExpireTime      uint32       // 邮件过期时间戳 秒
	IsRead          bool         // 是否已读
	IsAttachmentGot bool         // 是否已领取附件
	IsImportance    bool         // 是否重要邮件
	ItemList        [10]MailItem // 道具列表
}

type MailItem struct {
	ItemId int32
	Count  int32
}

func (m *Mail) GetTitle() string {
	var title strings.Builder
	for _, v := range m.Title {
		if v == 0 {
			break
		}
		title.WriteRune(v)
	}
	return title.String()
}

func (m *Mail) SetTitle(title string) {
	for i := 0; i < len(m.Title); i++ {
		m.Title[i] = 0
	}
	i := 0
	for _, val := range title {
		if i == len(m.Title) {
			logger.Error("title too long, title: %v", title)
			return
		}
		m.Title[i] = val
		i++
	}
}

func (m *Mail) GetContent() string {
	var content strings.Builder
	for _, v := range m.Content {
		if v == 0 {
			break
		}
		content.WriteRune(v)
	}
	return content.String()
}

func (m *Mail) SetContent(content string) {
	for i := 0; i < len(m.Content); i++ {
		m.Content[i] = 0
	}
	i := 0
	for _, val := range content {
		if i == len(m.Content) {
			logger.Error("content too long, title: %v", content)
			return
		}
		m.Content[i] = val
		i++
	}
}

func (m *Mail) GetSender() string {
	var sender strings.Builder
	for _, v := range m.Sender {
		if v == 0 {
			break
		}
		sender.WriteRune(v)
	}
	return sender.String()
}

func (m *Mail) SetSender(sender string) {
	for i := 0; i < len(m.Sender); i++ {
		m.Sender[i] = 0
	}
	i := 0
	for _, val := range sender {
		if i == len(m.Sender) {
			logger.Error("sender too long, title: %v", sender)
			return
		}
		m.Sender[i] = val
		i++
	}
}

func (m *Mail) GetItemList() []*MailItem {
	itemList := make([]*MailItem, 0)
	for i := 0; i < len(m.ItemList); i++ {
		item := &(m.ItemList[i])
		if item.ItemId == 0 {
			break
		}
		itemList = append(itemList, item)
	}
	return itemList
}

func (m *Mail) SetItemList(itemList []*MailItem) {
	for i := 0; i < len(m.ItemList); i++ {
		m.ItemList[i].ItemId = 0
		m.ItemList[i].Count = 0
	}
	for idx, item := range itemList {
		m.ItemList[idx].ItemId = item.ItemId
		m.ItemList[idx].Count = item.Count
	}
}

func (m *Mail) MemInit() {
	m.Id.NewVal = 0
	m.Id.OldVal = 0
	m.Id.Status = 0
	for i := 0; i < len(m.Title); i++ {
		m.Title[i] = 0
	}
	for i := 0; i < len(m.Content); i++ {
		m.Content[i] = 0
	}
	for i := 0; i < len(m.Sender); i++ {
		m.Sender[i] = 0
	}
	m.SendTime = 0
	m.ExpireTime = 0
	m.IsRead = false
	m.IsAttachmentGot = false
	m.IsImportance = false
	for i := 0; i < len(m.ItemList); i++ {
		m.ItemList[i].ItemId = 0
		m.ItemList[i].Count = 0
	}
}

func (m *Mail) IsUse() bool {
	return m.Id.NewVal != 0 || m.Id.OldVal != 0
}

func (m *Mail) GetId() int32 {
	return m.Id.NewVal
}

func (m *Mail) SetSync() {
	atomic.AddUint32(&(m.Id.Status), 1)
}

func (m *Mail) Clear() {
	m.Id.NewVal = 0
}

func (u *User) SetTimerMailId(mailId uint64) {
	u.TimerMailId = mailId
}

func (u *User) PacketUserMail(mail *Mail) *pb.Mail {
	itemList := mail.GetItemList()
	pbItemList := make([]*pb.Item, 0)
	for _, item := range itemList {
		pbItemList = append(pbItemList, &pb.Item{Id: item.ItemId, Num: item.Count})
	}
	return &pb.Mail{
		MailId:          mail.GetId(),
		Title:           mail.GetTitle(),
		Content:         mail.GetContent(),
		Sender:          mail.GetSender(),
		SendTime:        mail.SendTime,
		ExpireTime:      mail.ExpireTime,
		IsRead:          mail.IsRead,
		IsAttachmentGot: mail.IsAttachmentGot,
		IsImportance:    mail.IsImportance,
		ItemList:        pbItemList,
	}
}

func (u *User) GetMailList() []*Mail {
	mailList := make([]*Mail, 0)
	for i := 0; i < len(u.Mail); i++ {
		if !u.Mail[i].IsUse() {
			continue
		}
		mailList = append(mailList, &u.Mail[i])
	}
	return mailList
}

func (u *User) GetMail(mailId int32) *Mail {
	for i := 0; i < len(u.Mail); i++ {
		if u.Mail[i].GetId() == mailId {
			return &u.Mail[i]
		}
	}
	return nil
}

func (u *User) findOneDelMail() *Mail {
	sortMailList := make([]*Mail, 0)
	for i := 0; i < len(u.Mail); i++ {
		sortMailList = append(sortMailList, &(u.Mail[i]))
	}
	sort.Slice(sortMailList, func(i, j int) bool {
		return sortMailList[i].SendTime < sortMailList[j].SendTime
	})
	for _, mail := range sortMailList {
		if mail.IsRead && len(mail.GetItemList()) == 0 {
			return mail
		}
	}
	for _, mail := range sortMailList {
		if mail.IsRead && len(mail.GetItemList()) != 0 {
			return mail
		}
	}
	for _, mail := range sortMailList {
		if !mail.IsRead && len(mail.GetItemList()) == 0 {
			return mail
		}
	}
	for _, mail := range sortMailList {
		if !mail.IsRead && len(mail.GetItemList()) != 0 {
			return mail
		}
	}
	return nil
}

func (u *User) AddMail(title string, content string, sender string, expireTime uint32, isImportance bool, itemMap map[int32]int32) (bool, int32) {
	if expireTime != 0 && expireTime < uint32(MyShm.TimeNow) {
		logger.Error("mail already expired, expireTime: %v, uid: %v", expireTime, u.GetUid())
		return false, 0
	}
	u.ClearAllExpireMail()
	var targetMail *Mail = nil
	for i := 0; i < len(u.Mail); i++ {
		if u.Mail[i].IsUse() {
			continue
		}
		targetMail = &(u.Mail[i])
	}
	if targetMail == nil {
		logger.Info("user mail list is full, uid: %v", u.GetUid())
		mail := u.findOneDelMail()
		if mail == nil {
			logger.Error("find one del mail fail, uid: %v", u.GetUid())
			return false, 0
		}
		logger.Info("del mail, mailId: %v, title: %v, uid: %v", mail.GetId(), mail.GetTitle(), u.GetUid())
		targetMail = mail
	}
	if len(itemMap) > len(targetMail.ItemList) {
		logger.Error("mail item list too long, len: %v, uid: %v", len(itemMap), u.GetUid())
		return false, 0
	}
	if utf8.RuneCountInString(title) > len(targetMail.Title) {
		logger.Error("mail title too long, len: %v, uid: %v", len(title), u.GetUid())
		return false, 0
	}
	if utf8.RuneCountInString(content) > len(targetMail.Content) {
		logger.Error("mail content too long, len: %v, uid: %v", len(content), u.GetUid())
		return false, 0
	}
	if utf8.RuneCountInString(sender) > len(targetMail.Sender) {
		logger.Error("mail sender too long, len: %v, uid: %v", len(sender), u.GetUid())
		return false, 0
	}
	u.CurrMailId++
	targetMail.SetTitle(title)
	targetMail.SetContent(content)
	targetMail.SetSender(sender)
	targetMail.SendTime = uint32(MyShm.TimeNow)
	targetMail.ExpireTime = expireTime
	targetMail.IsRead = false
	targetMail.IsAttachmentGot = false
	targetMail.IsImportance = isImportance
	itemList := make([]*MailItem, 0)
	for itemId, count := range itemMap {
		itemList = append(itemList, &MailItem{ItemId: itemId, Count: count})
	}
	targetMail.SetItemList(itemList)
	targetMail.Id.NewVal = u.CurrMailId
	logger.Debug("add mail ok, title: %v, uid: %v", title, u.GetUid())
	return true, targetMail.GetId()
}

func (u *User) DelMail(mailId int32) {
	for i := 0; i < len(u.Mail); i++ {
		if u.Mail[i].GetId() == mailId {
			u.Mail[i].Clear()
		}
	}
}

func (u *User) ClearAllExpireMail() {
	for i := 0; i < len(u.Mail); i++ {
		mail := &(u.Mail[i])
		if !mail.IsUse() {
			continue
		}
		if mail.ExpireTime == 0 {
			continue
		}
		if MyShm.TimeNow > int64(mail.ExpireTime) {
			mail.Clear()
		}
	}
}
